package io.itit.ecp.admin.server.service.impl;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import icu.mhb.mybatisplus.plugln.base.service.impl.JoinServiceImpl;
import io.itit.ecp.admin.server.entity.*;
import io.itit.ecp.admin.server.entity.extend.PhysicalModelExt;
import io.itit.ecp.admin.server.mapper.DeviceWarningRecordMapper;
import io.itit.ecp.admin.server.service.*;
import io.itit.grass.core.server.service.IUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceWarningRecordServiceImpl extends JoinServiceImpl<DeviceWarningRecordMapper, DeviceWarningRecordEntity>
        implements IDeviceWarningRecordService, SmartInitializingSingleton {

    private IDeviceWarningPushUserService deviceWarningPushUserService;

    private final IDeviceWarningService deviceWarningService;

    private final IDeviceWarningThresholdService deviceWarningThresholdService;

    private final IPhysicalModelService physicalModelService;

    private final IDeviceWarningUserService deviceWarningUserService;

    private final IDeviceService deviceService;

    private final IUserService userService;

    @Override
    public void afterSingletonsInstantiated() {
        this.deviceWarningPushUserService = SpringUtil.getBean(IDeviceWarningPushUserService.class);
    }

    @Override
    @Transactional
    public boolean removeById(Serializable id) {
        deviceWarningPushUserService.removeByWarningRecordId(id);
        return super.removeById(id);
    }

    @Override
    @Transactional
    public boolean removeBatchByIds(Collection<?> ids, int batchSize) {
        deviceWarningPushUserService.removeByWarningRecordIds(ids);
        return super.removeBatchByIds(ids, batchSize);
    }

    @Override
    public void triggerWarning(String deviceId, List<PhysicalModelExt> thresholds) {
        if (StringUtils.isNotEmpty(deviceId) && CollectionUtils.isNotEmpty(thresholds)) {
            Date current = new Date();
            List<DeviceWarningEntity> deviceWarningEntities = deviceWarningService.list(new LambdaQueryWrapper<DeviceWarningEntity>()
                    .le(DeviceWarningEntity::getStartTime, current)
                    .ge(DeviceWarningEntity::getEndTime, current));
            for (DeviceWarningEntity warning : deviceWarningEntities) {
                List<DeviceWarningThresholdEntity> warningThresholdEntities = deviceWarningThresholdService.findByDeviceWarningId(warning.getId());
                for (PhysicalModelExt modelExt : thresholds) {
                    Object value = modelExt.getNewValue();
                    if (StringUtils.isNotEmpty(deviceId) && Objects.nonNull(value)) {
                        log.info("warning:{}, deviceId={}, thresholdValue={}", warning.getId(), deviceId, value);
                        List<DeviceWarningThresholdEntity> thresholdEntities = warningThresholdEntities
                                .stream()
                                .filter(item -> StringUtils.equals(item.getDeviceId(), deviceId) &&
                                        StringUtils.equals(item.getModelId(), modelExt.getId()) && matcherThreshold(item, modelExt))
                                .collect(Collectors.toList());
                        if (!thresholdEntities.isEmpty()) {
                            doTriggerWarning(warning, thresholdEntities, current, value);
                        }
                    }
                }
            }
        }
    }

    private boolean matcherThreshold(DeviceWarningThresholdEntity thresholdEntity, PhysicalModelExt modelExt) {
        String thresholdValue = thresholdEntity.getThresholdValue();
        Object value = modelExt.getNewValue();
        String judgeRule = thresholdEntity.getJudgeRule();
        log.info("matcherThreshold: DeviceWarningThresholdEntityId:{} PhysicalModelExtId:{},judgeRule:{}," +
                "thresholdValue:{}, value:{}", thresholdEntity.getId(), modelExt.getId(), judgeRule, thresholdValue, value);
        if (StringUtils.isNotEmpty(judgeRule)) {
            try {
                BigDecimal thresholdValueBig = new BigDecimal(thresholdValue);
                BigDecimal valueBig = new BigDecimal(String.valueOf(value));
                if ("1".equals(judgeRule)) { // >=
                    return thresholdValueBig.compareTo(valueBig) >= 0;
                } else if ("2".equals(judgeRule)) { // <=
                    return thresholdValueBig.compareTo(valueBig) <= 0;
                }
            } catch (NumberFormatException e) {
                log.warn("NumberFormatException: DeviceWarningThresholdEntityId:{} PhysicalModelExtId:{}," +
                        "thresholdValue:{}, value:{}", thresholdEntity.getId(), modelExt.getId(), thresholdValue, value);
                return false;
            }
        }
        return StringUtils.equals(thresholdValue, String.valueOf(value));
    }

    private void doTriggerWarning(DeviceWarningEntity warning, List<DeviceWarningThresholdEntity> thresholds, Date warningDate, Object thresholdValue) {
        List<DeviceWarningUserEntity> userEntities = deviceWarningUserService.findByDeviceWarningId(warning.getId());
        DeviceWarningRecordEntity deviceWarningRecord = new DeviceWarningRecordEntity();
        deviceWarningRecord.setDeviceWarningId(warning.getId());
        deviceWarningRecord.setWarningName(warning.getWarningName());
        deviceWarningRecord.setSceneProjectId(warning.getSceneProjectId());
        deviceWarningRecord.setSceneEdificeId(warning.getSceneEdificeId());
        deviceWarningRecord.setSceneFloorId(warning.getSceneFloorId());
        deviceWarningRecord.setSceneRoomId(warning.getSceneRoomId());
        deviceWarningRecord.setThresholdLevel(warning.getThresholdLevel());
        deviceWarningRecord.setWarningDate(warningDate);
        deviceWarningRecord.setStatus(userEntities.isEmpty() ? "3" : "1");
        deviceWarningRecord.setDeptId(warning.getDeptId());
        List<Map<String, Object>> details = thresholds.stream().map(item -> {
            Map<String, Object> map = new HashMap<>();
            map.put("deviceId", item.getDeviceId());
            map.put("deviceName", deviceService.getById(item.getDeviceId()).getDeviceName());
            map.put("modelId", item.getModelId());
            map.put("modelName", physicalModelService.getById(item.getModelId()).getModelName());
            map.put("thresholdValue", formatValue(thresholdValue)); //  @see io.itit.ecp.admin.server.entity.DeviceWarningRecordEntity.DeviceDetail
            return map;
        }).collect(Collectors.toList());
        deviceWarningRecord.setDeviceDetails(JSON.toJSONString(details));
        this.save(deviceWarningRecord);
        List<DeviceWarningPushUserEntity> list = userEntities
                .stream().map(item -> {
                    DeviceWarningPushUserEntity pushUserEntity = new DeviceWarningPushUserEntity();
                    pushUserEntity.setDeviceWarningId(warning.getId());
                    pushUserEntity.setDeviceWarningRecordId(deviceWarningRecord.getId());
                    pushUserEntity.setUserId(item.getUserId());
                    pushUserEntity.setUserName(userService.getById(item.getUserId()).getUserName());
                    pushUserEntity.setStatus("1");
                    return pushUserEntity;
                }).collect(Collectors.toList());
        deviceWarningPushUserService.saveBatch(list);
    }

    private Object formatValue(Object value) {
        try {
            return new BigDecimal(String.valueOf(value))
                    .setScale(2, RoundingMode.HALF_UP);
        } catch (NumberFormatException e) {
            return value;
        }
    }
}
